import { useEffect, useRef, useState } from "react";
import { Message } from "../model/bindings/Message";
import {
  EuiButton,
  EuiButtonIcon,
  EuiFlexGroup,
  EuiFlexItem,
  EuiTextArea,
  useEuiTheme,
} from "@elastic/eui";
import {
  checkMessageLength,
  getChats,
  getUsers,
  markAsRead,
  submitMessage,
} from "../commands/chats";
import { useMessageStore } from "../state/messages";
import { useDraftStore } from "../state/drafts";
import { formatDateTime, MessageStatus } from "./MessageStatus";
import { UserStatus } from "../model/bindings/UserStatus";
import { ChatHeader } from "./ChatHeader";
import { palette } from "../styles/palette";
import { sizes } from "../styles/sizes";
import { CustomExpiryModal } from "./CustomExpiryModal.tsx";
import { PerMessageMenu } from "./PerMessageMenu.tsx";
import moment from "moment";
import { useUserStore } from "../state/users.ts";
import {
  ExpiringMessageIcon,
  ExpiringMessageUrgency,
  NEAR_EXPIRY_DAYS,
  URGENT_EXPIRY_HOURS,
} from "./ExpiringMessageIcon.tsx";

export type ChatProps = {
  userAutogeneratedName: string;
  messages: Message[];
  userReplyKey: string;
  currentUserStatus: UserStatus;
  userAlias: string | null;
  userDescription: string | null;
  markAsUnread: () => void;
  showEditModal: () => void;
  showMuteModal: () => void;
  showCopyToClipboardModal: () => void;
};

type ChatMessageUniqueId = `${Message["type"]}${Message["id"]}`;

export const Chat = ({
  userAutogeneratedName,
  messages,
  userReplyKey,
  currentUserStatus,
  userAlias,
  userDescription,
  markAsUnread,
  showEditModal,
  showMuteModal,
  showCopyToClipboardModal,
}: ChatProps) => {
  const [messageFillRatio, setMessageFillRatio] = useState(0);
  const [currentMessageDraft, setCurrentMessageDraft] = useState("");

  const [maybeContextMenuOpenForMessage, setMaybeContextMenuOpenForMessage] =
    useState<ChatMessageUniqueId | null>(null);

  const [
    maybeMessageToSetCustomExpiryFor,
    setMaybeMessageToSetCustomExpiryFor,
  ] = useState<Message | null>(null);

  const messagesEndRef = useRef<HTMLDivElement>(null);
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  const draftStore = useDraftStore();
  const messageStore = useMessageStore();
  const userStore = useUserStore();

  const prevUserReplyKey = useRef<string | null>(null);

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "instant" });
  };

  const { euiTheme } = useEuiTheme();
  const { size } = euiTheme;

  useEffect(() => {
    const has_unread_messages = messages.some(
      (m) =>
        m.type === "userToJournalistMessage" &&
        m.userPk === userReplyKey &&
        !m.read,
    );
    const is_marked_as_unread = userStore.users?.find?.(
      (user) => user.userPk === userReplyKey,
    )?.markedAsUnread;

    if (has_unread_messages || is_marked_as_unread) {
      markAsRead(userReplyKey);
    }

    getChats().then(messageStore.setMessages);
    getUsers().then(userStore.setUsers);

    if (userReplyKey !== prevUserReplyKey.current) {
      const draft = draftStore.drafts[userReplyKey] || "";
      setCurrentMessageDraft(draft);

      scrollToBottom();
    }

    prevUserReplyKey.current = userReplyKey;
  }, [userReplyKey]);

  const adjustHeight = () => {
    const textarea = textareaRef.current;
    if (textarea !== null) {
      // Reset height to default.
      // Without this it doesn't shrink back when deleting lines
      textarea.style.height = "auto";
      // Clamp to 121px which is ~5 rows
      const targetHeight =
        textarea.scrollHeight < 121 ? textarea.scrollHeight : 121;
      textarea.style.height = `${targetHeight}px`;
    }
  };

  useEffect(() => {
    adjustHeight();
  }, [currentMessageDraft]);

  const submitMessageForm = async () => {
    try {
      await submitMessage(userReplyKey, currentMessageDraft);

      setCurrentMessageDraft("");
      draftStore.clearDraft(userReplyKey);
      setMessageFillRatio(0);

      const messages = await getChats();
      messageStore.setMessages(messages);
    } catch (e: unknown) {
      if (e instanceof Error) {
        console.error("Error submitting message:", e.message);
      } else {
        console.error("Error submitting message", e);
      }
    }
  };

  return (
    <EuiFlexGroup direction="column">
      <ChatHeader
        userAlias={userAlias}
        userAutogeneratedName={userAutogeneratedName}
        userDescription={userDescription ? userDescription : ""}
        currentUserStatus={currentUserStatus}
        showMuteModal={showMuteModal}
        showEditModal={showEditModal}
        showCopyToClipboardModal={showCopyToClipboardModal}
        markAsUnread={markAsUnread}
      />

      {/* Chat messages */}
      <EuiFlexItem grow={true}>
        <EuiFlexGroup
          direction="column"
          style={{
            margin: size.m,
            height: "100%",
            overflowY: "auto", // Make messages scrollable
            paddingBottom: size.base, // Add some padding at the bottom
          }}
        >
          {messages
            .filter((m) => {
              return m.userPk === userReplyKey;
            })
            .map((m) => {
              const alignSelf =
                m.type === "journalistToUserMessage" ? "end" : "start";
              const background =
                m.type === "journalistToUserMessage"
                  ? palette("journalist-to-user-message-background")
                  : palette("user-to-journalist-message-background");
              const color =
                m.type === "journalistToUserMessage"
                  ? palette("journalist-to-user-message-color")
                  : palette("user-to-journalist-message-color");
              const isSent =
                m.type === "journalistToUserMessage" ? m.isSent : null;

              const uniqueMessageId: ChatMessageUniqueId = `${m.type}${m.id}`;

              const now = moment();
              const expiry = moment(m.customExpiry ?? m.normalExpiry);

              const timeUntilExpiry = moment.duration(expiry.diff(now));

              const nearExpiry = timeUntilExpiry.asDays() < NEAR_EXPIRY_DAYS;

              const urgentExpiry =
                timeUntilExpiry.asHours() < URGENT_EXPIRY_HOURS;

              const expiringMessageUrgency: ExpiringMessageUrgency =
                urgentExpiry ? "URGENT" : nearExpiry ? "NEAR" : undefined;

              return (
                <EuiFlexItem
                  grow={false}
                  style={{
                    alignSelf,
                    marginBottom: `-${size.base}`,
                    maxWidth: "85%",
                    "&:hover": {
                      transform: "scale(1.5)",
                    },
                  }}
                  key={uniqueMessageId}
                >
                  <EuiFlexGroup
                    direction={
                      m.type === "journalistToUserMessage"
                        ? "rowReverse"
                        : "row"
                    }
                    gutterSize="xs"
                    alignItems="center"
                  >
                    <div
                      style={{
                        whiteSpace: "pre-wrap",
                        overflowWrap: "break-word",
                        maxWidth: "100%",
                        alignSelf,
                        color,
                        background,
                        padding: size.m,
                        width: "fit-content",
                        minWidth: sizes.chatMessage.minWidth,
                        borderRadius: size.s,
                      }}
                    >
                      {m.message}
                    </div>
                    <PerMessageMenu
                      isOpen={
                        uniqueMessageId === maybeContextMenuOpenForMessage
                      }
                      setIsOpen={(isOpen) =>
                        setMaybeContextMenuOpenForMessage(
                          isOpen ? uniqueMessageId : null,
                        )
                      }
                      openCustomExpiryModal={() =>
                        setMaybeMessageToSetCustomExpiryFor(m)
                      }
                    />
                    {m.customExpiry && (
                      <EuiButtonIcon
                        iconType={"clockCounter"}
                        color="primary"
                        title={`Custom expiry (${formatDateTime(m.customExpiry)}).\nClick to clear/adjust custom expiry...`}
                        aria-label={"Clear/adjust custom expiry"}
                        onClick={() => setMaybeMessageToSetCustomExpiryFor(m)}
                      />
                    )}
                    {nearExpiry && (
                      <ExpiringMessageIcon
                        expiringMessageUrgency={expiringMessageUrgency}
                        context="CHAT"
                      />
                    )}
                  </EuiFlexGroup>
                  <div
                    style={{
                      alignSelf,
                      fontSize: size.m,
                      color: palette("message-status-color"),
                      marginTop: size.xs,
                    }}
                  >
                    <MessageStatus
                      isSent={isSent}
                      timestamp={
                        m.type === "journalistToUserMessage"
                          ? m.sentAt
                          : m.receivedAt
                      }
                    />
                  </div>
                </EuiFlexItem>
              );
            })}
          <div ref={messagesEndRef} />
        </EuiFlexGroup>
      </EuiFlexItem>

      {/* Send message form */}
      <EuiFlexItem grow={false} style={{ position: "sticky", bottom: 0 }}>
        <EuiFlexGroup
          style={{
            background: palette("message-sending-form-background"),
            padding: size.m,
          }}
        >
          <div
            style={{
              position: "relative",
              width: "100%",
              overflow: "hidden",
            }}
          >
            <EuiTextArea
              inputRef={textareaRef}
              style={{
                // @ts-expect-error Hide the focus colour provided by EUI
                "--euiFormControlStateColor": "transparent",
                marginBottom: size.xxs, // Progress bar margin
                paddingBottom: size.xs,
                minHeight: sizes.chatInput.minHeight,
              }}
              placeholder="Enter a message..."
              fullWidth
              value={currentMessageDraft}
              resize="none"
              rows={1}
              onChange={(e) => {
                const message = e.target.value;

                if (message.length != 0) {
                  checkMessageLength(message).then((ratio) =>
                    setMessageFillRatio(ratio),
                  );
                } else {
                  setMessageFillRatio(0);
                }

                draftStore.setDraft(userReplyKey, message);
                setCurrentMessageDraft(message);
              }}
            />
            <div
              style={{
                position: "absolute",
                bottom: size.xxs,
                // These values are based off of the roundness of the parent containers border radius
                left: "3px",
                right: "3px",
                height: size.xxs,
                background: palette("message-sending-progress-bar-background"),
              }}
            >
              <div
                style={{
                  width: `${Math.min(messageFillRatio * 100, 100)}%`,
                  height: "100%",
                  background:
                    messageFillRatio > 1
                      ? palette("message-sending-full-progress-bar-background")
                      : palette(
                          "message-sending-filling-progress-bar-background",
                        ),
                  transition:
                    "width 0.2s ease-out, background-color 0.2s ease-out",
                }}
              />
            </div>
          </div>
          <EuiButton
            type="submit"
            style={{ height: "100%" }}
            disabled={!currentMessageDraft || messageFillRatio > 1}
            onClick={submitMessageForm}
          >
            Submit
          </EuiButton>
        </EuiFlexGroup>
      </EuiFlexItem>
      <CustomExpiryModal
        close={() => setMaybeMessageToSetCustomExpiryFor(null)}
        maybeMessageToSetCustomExpiryFor={maybeMessageToSetCustomExpiryFor}
      />
    </EuiFlexGroup>
  );
};
